package org.skatiger.jrove.spring.tags.component;

import java.io.IOException;
import java.util.Collection;
import java.util.Locale;

import javax.el.ValueExpression;
import javax.management.ValueExp;
import javax.servlet.jsp.JspException;

import org.skatiger.jrove.JroveException;
import org.skatiger.jrove.ResponseWriter;
import org.skatiger.jrove.ViewContext;
import org.skatiger.jrove.spring.tags.SpringComponent;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.util.StringUtils;
import org.springframework.web.util.HtmlUtils;

public class Message extends SpringComponent {
    public static final String DEFAULT_ARGUMENT_SEPARATOR = ",";

    private Object message;

    private String code;

    private Object arguments;

    private String argumentSeparator = DEFAULT_ARGUMENT_SEPARATOR;

    private String text;

    private String var;

    public Object getMessage() {
        return getPropertyValue("message", message);
    }

    public void setMessage(Object message) {
        this.message = message;
    }

    public String getCode() {
        return getPropertyValue("code", code);
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Object getArguments() {
        return getPropertyValue("arguments", arguments);
    }

    public void setArguments(Object arguments) {
        this.arguments = arguments;
    }

    public String getArgumentSeparator() {
        return getPropertyValue("argumentSeparator", argumentSeparator);
    }

    public void setArgumentSeparator(String argumentSeparator) {
        this.argumentSeparator = argumentSeparator;
    }

    public String getText() {
        return getPropertyValue("text", text);
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getVar() {
        return getPropertyValue("var", var);
    }

    public void setVar(String var) {
        this.var = var;
    }

    /**
     * Resolve the given arguments Object into an arguments array.
     * 
     * @param arguments
     *            the specified arguments Object
     * @return the resolved arguments as array
     * @throws JspException
     *             if argument conversion failed
     * @see #setArguments
     */
    @SuppressWarnings("rawtypes")
    protected Object[] resolveArguments(Object arguments) throws JroveException {
        if (arguments instanceof String) {
            String argSep = getArgumentSeparator();
            String[] stringArray = StringUtils.delimitedListToStringArray(
                    (String) arguments, argSep);
            return stringArray;
        } else if (arguments instanceof Object[]) {
            return (Object[]) arguments;
        } else if (arguments instanceof Collection) {
            return ((Collection) arguments).toArray();
        } else if (arguments != null) {
            // Assume a single argument object.
            return new Object[] { arguments };
        } else {
            return null;
        }
    }

    /**
     * Use the theme MessageSource for theme message resolution.
     */
    protected MessageSource getMessageSource() {
        return getRequestContext().getMessageSource();
    }

    /**
     * Resolve the specified message into a concrete message String. The
     * returned message String should be unescaped.
     */
    public String resolveMessage() throws JroveException,
            NoSuchMessageException {
        MessageSource messageSource = getMessageSource();
        if (messageSource == null) {
            throw new NullPointerException(
                    "No corresponding MessageSource found");
        }
        Locale locale = getRequestContext().getLocale();
        // Evaluate the specified MessageSourceResolvable, if any.
        Object message = getMessage();
        MessageSourceResolvable resolvedMessage = null;
        if (message instanceof MessageSourceResolvable) {
            resolvedMessage = (MessageSourceResolvable) message;
        }

        if (resolvedMessage != null) {
            // We have a given MessageSourceResolvable.
            return messageSource.getMessage(resolvedMessage, locale);
        }
        String code = getCode();
        String text = getText();
        if (code != null || text != null) {
            // We have a code or default text that we need to resolve.
            Object arguments = getArguments();
            Object[] argumentsArray = resolveArguments(arguments);
            if (text != null) {
                // We have a fallback text to consider.
                return messageSource.getMessage(code, argumentsArray, text,
                        locale);
            } else {
                // We have no fallback text to consider.
                return messageSource.getMessage(code, argumentsArray, locale);
            }
        }
        throw new IllegalArgumentException(
                "message component required message, code or text attribute for renderer");
        // All we have is a specified literal text.
    }

    @Override
    public void encodeBegin(ViewContext context) throws IOException {
        try {
            // Resolve the unescaped message.
            String msg = resolveMessage();
            // HTML and/or JavaScript escape, if demanded.
            msg = isHtmlEscape() ? HtmlUtils.htmlEscape(msg) : msg;
            // Expose as variable, if demanded, else write to the page.
            String resolvedVar = getVar();
            if (resolvedVar != null) {
                context.setAttribute(resolvedVar, msg);
            } else {
                ResponseWriter writer = context.getResponseWriter();
                writer.writeText(msg, null);
            }
        } catch (NoSuchMessageException ex) {
            throw new JroveException(getNoSuchMessageExceptionDescription(ex));
        }
    }

    protected String getNoSuchMessageExceptionDescription(
            NoSuchMessageException ex) {
        StringBuilder builder = new StringBuilder(ex.getMessage());
        Object code = this.code;
        if (this.code == null) {
            ValueExpression codeVE = getValueExpression("code");
            builder.append(" Message code is ValueExpression , "); 
            builder.append(codeVE.toString());
        } 
        return builder.toString();
    }

}
